home *** CD-ROM | disk | FTP | other *** search
- /*
- * parsemime.c -- functions to parse incoming mail messages in
- * MIME format
- *
- * 3-17-93 weber@eitech.com really fixed trailing newline bug in from7bit
- * 2-25-93 weber@eitech.com fixed trailing newline bug (thanks Bob Sum)
- * 2-18-93 weber@eitech.com added extraction of x-splitsize: field
- * 2-18-93 weber@eitech.com added extraction of apparently-to: field
- * 1-21-93 weber@eitech.com adding extraction of To: field
- * 9-8-92 ekr@eitech.com v 1.1
- * 27-Jun-92 weber@eitech.com marked ServiceMail(tm) v1.0
- * 25-May-92 weber@eitech.com created
- *
- * Copyright (c) 1992 Enterprise Integration Technologies Corporation
- *
- * Permission to use, copy, modify, distribute, and sell this software and
- * its documentation for any purpose is hereby granted without fee, provided
- * that (i) the above copyright notices and this permission notice appear in
- * all copies of the software and related documentation, and (ii) the name of
- * Enterprise Integration Technologies Corporation may not be used in any
- * advertising or publicity relating to the software without the specific,
- * prior written permission of Enterprise Integration Technologies Corporation.
- *
- * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
- * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
- *
- * IN NO EVENT SHALL ENTERPRISE INTEGRATION TECHNOLOGIES CORPORATION BE
- * LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
- * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY
- * THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
- #include <pwd.h>
- #include <sys/types.h>
- #include <sys/dir.h>
-
- #include "mesh.h"
-
- #ifndef TEMPDIR
- #define TEMPDIR /tmp
- #endif
- char scratchdir[MAXFILENAMELEN];
-
- char *Trim(s)
- char *s;
- {
- char *t;
-
- t = s + strlen(s);
- while (t-- > s && isspace(*t)) *t = NULL;
- return s;
- }
-
- HandleMessage(instream)
- FILE *instream;
- {
- struct MessageInfo minfo;
- char killdircmd[MAXFILENAMELEN+16];
- char origdir[MAXFILENAMELEN];
- /*getcwd() is lame. Use it if you must(like you don't have getwd())*/
- getwd(origdir);
- sprintf(scratchdir, "%s/mesh.%d", TEMPDIR, getpid());
- mkdir(scratchdir, 0700);
- chdir(scratchdir);
-
- InitHeader(&minfo);
- ParseHeader(&minfo, instream);
- ParseBody(&minfo, instream, NULL, NULL);
- if (strcasecmp(minfo.content.type, "message") ||
- strcasecmp(minfo.content.format, "partial")) StartJob(&minfo);
-
- sprintf(killdircmd, "rm -r %s", scratchdir);
- system(killdircmd);
- FreeGarbage(&minfo);
- chdir(origdir);
- }
-
- InitHeader(message)
- struct MessageInfo *message;
- {
- message->from = message->reply_to = message->to = NULL;
- message->id = message->subject = message->splitsize = NULL;
- message->service = message->date = NULL;
- message->content.id = message->content.format = NULL;
- message->content.encoding = message->content.type = NULL;
- message->content.numparms = 0;
- }
-
- ParseHeader(message, input)
- struct MessageInfo *message;
- FILE *input;
- {
- char *s,*c;
- char *Trim(), *malloc(), *fgets(), *strtok();
- char buf[MAXLINELEN],f;
-
- while (fgets(buf, MAXLINELEN, input) != NULL && *Trim(buf))
- {
- while(((f=ungetc(fgetc(input),input))=='\t')||(f==' '))
- {
- char foo[MAXLINELEN];
- /*Get the next line and append it to buf*/
-
- fgets(foo,MAXLINELEN-strlen(buf),input);
- for(c=foo;isspace(*c);c++);
- if(*Trim(c))
- strcpy(buf+strlen(buf),c);
- else
- ungetc('\n',input);
- }
- if (!strncasecmp(buf, "from:", 5)) {
- StoreAddress(buf+5, &(message->from));
- }
- else if (!strncasecmp(buf, "to:", 3)) {
- StoreAddress(buf+3, &(message->to));
- }
- else if (!strncasecmp(buf, "apparently-to:", 14)) {
- StoreAddress(buf+14, &(message->to));
- }
- else if (!strncasecmp(buf, "x-splitsize:", 12)) {
- StoreAddress(buf+12, &(message->splitsize));
- }
- else if (!strncasecmp(buf, "reply-to:", 9)) {
- StoreAddress(buf+9, &(message->reply_to));
- }
- else if (!strncasecmp(buf, "message-id:", 11)) {
- StoreString(buf+11, &(message->id));
- }
- else if (!strncasecmp(buf, "date:", 5)) {
- StoreString(buf+5, &(message->date));
- }
- else if (!strncasecmp(buf, "content-id:", 11)) {
- StoreString(buf+11, &(message->content.id));
- }
- else if (!strncasecmp(buf, "subject:", 8)) {
- StoreString(buf+8, &(message->subject));
- }
- else if (!strncasecmp(buf, "content-description:", 20)) {
- StoreString(buf+20, &(message->subject));
- }
- else if (!strncasecmp(buf, "x-service:", 10)) {
- StoreString(buf+10, &(message->service));
- }
- else if (!strncasecmp(buf, "content-type:", 13)) {
- StoreString(strtok(buf+13, " /\n\t"), &(message->content.type));
- StoreString(strtok(NULL, " ;\n\t"), &(message->content.format));
- while (s = strtok(NULL, " ,=\n\t")) {
- StoreString(s, &(message->content.parms[message->content.numparms].name));
- s += strlen(s) + 1;
- while (*s == ' ' || *s == '\t' || *s == '=') s++;
- StoreString(strtok(NULL, (*s == '"' ? "\"\n" : ";,\n\t")),
- &(message->content.parms[message->content.numparms++].value));
- }
- }
- else if (!strncasecmp(buf, "content-transfer-encoding:", 26)) {
- StoreString(buf+26, &(message->content.encoding));
- }
- }
- if (message->content.type == NULL) {
- StoreString("text", &(message->content.type));
- StoreString("plain", &(message->content.format));
- }
- }
-
- StoreAddress(rval, lval)
- char *rval, **lval;
- {
- char *s, *t, *strtok(), *strchr();
-
- if (s = strchr(rval, '<'))
- if (s = strtok(s+1, " \t\n>")) StoreString(s, lval);
- else ErrorMsg("cannot parse posterior address");
- else
- if (s = strtok(rval, " \t\n,")) StoreString(s, lval);
- else ErrorMsg("cannot parse anterior address");
- return;
- }
-
- StoreString(rval, lval)
- char *rval, **lval;
- {
- if (rval == NULL) {
- *lval = NULL;
- return;
- }
- if (*lval != NULL) free(*lval);
- while (isspace(*rval)) rval++;
- *lval = malloc(strlen(rval) + 1);
- if (*lval == NULL) ErrorExit("malloc failed");
- strcpy(*lval, rval);
- }
-
- ParseBody(message, input, boundaries, numboundlval)
- struct MessageInfo *message;
- FILE *input;
- char **boundaries;
- int *numboundlval;
- {
- if (!strcasecmp(message->content.type, "multipart"))
- ParseMultipart(message, input);
- else if (!strcasecmp(message->content.type, "message") &&
- !strcasecmp(message->content.format, "external-body"))
- ParseExternal(message, input);
- else if (!strcasecmp(message->content.type, "message") &&
- !strcasecmp(message->content.format, "partial"))
- ParsePartial(message, input);
- else {
- if (message->content.id) strcpy(message->body.fname, message->content.id);
- else MakeFileName(message->body.fname);
- StoreStream(message->content.encoding, input, message->body.fname,
- boundaries, numboundlval);
- }
- }
-
- ParseMultipart(message, input)
- struct MessageInfo *message;
- FILE *input;
- {
- int moreparts;
- int bindex;
- char buf[1024];
-
- for(bindex=0; bindex < message->content.numparms; bindex++)
- if (!strcasecmp(message->content.parms[bindex].name, "boundary")) break;
- if (bindex >= message->content.numparms)
- ErrorExit("multipart missing boundary parameter");
-
- /*Tack on the mandatory '--' to the boundary*/
- sprintf(buf,"--%s",message->content.parms[bindex].value);
- strcpy(message->content.parms[bindex].value,buf);
- /* burn off prologue */
- moreparts = 1;
- from7bit(input, NULL, &(message->content.parms[bindex].value), &moreparts);
-
- /* identify, parse message, and store each part (there may be none) */
- message->body.multipart.parts =
- (struct MessageInfo **) calloc(MAXPARTS, sizeof(struct MessageInfo *));
- message->body.multipart.numparts = 0;
- while (moreparts && message->body.multipart.numparts < MAXPARTS) {
- struct MessageInfo *m;
-
- message->body.multipart.parts[message->body.multipart.numparts] =
- m = (struct MessageInfo *) malloc(sizeof(struct MessageInfo));
- if (m == NULL) ErrorExit("malloc of messageinfo failed");
- InitHeader(m);
- ParseHeader(m, input);
- ParseBody(m, input, &(message->content.parms[bindex].value), &moreparts);
- message->body.multipart.numparts++;
- }
- if (moreparts) ErrorMsg("too many parts");
-
- /* ignore epilogue */
- }
-
- ParseExternal(message, input)
- struct MessageInfo *message;
- FILE *input;
- {
- char buf[256], newfile[MAXFILENAMELEN];
- FILE *fopen(), *f;
- int ai, ni, di, si, mi, i;
-
- ai = ni = di = si = mi = -1;
- for(i=0; i < message->content.numparms; i++)
- if (!strcasecmp(message->content.parms[i].name, "access-type")) ai=i;
- else if (!strcasecmp(message->content.parms[i].name, "name")) ni=i;
- else if (!strcasecmp(message->content.parms[i].name, "site")) si=i;
- else if (!strcasecmp(message->content.parms[i].name, "directory")) di=i;
- else if (!strcasecmp(message->content.parms[i].name, "mode")) mi=i;
- if (ai < 0 || ni < 0) ErrorExit("external-body must have an access-type and name");
-
- MakeFileName(newfile);
- sprintf(buf, "grabexternal %s %s %s %s %s %s", newfile,
- message->content.parms[ai].value, message->content.parms[ni].value,
- si>=0 ? message->content.parms[si].value : "",
- si>=0 && di>=0 ? message->content.parms[di].value : "",
- si>=0 && di>=0 && mi>=0 ? message->content.parms[mi].value : "");
- system(buf);
- message->content.id = message->content.format = NULL;
- message->content.encoding = message->content.type = NULL;
- message->content.numparms = 0;
- ParseHeader(message, input);
- f = fopen(newfile, "r");
- if (f == NULL) ErrorMsg("can't get external file");
- else {
- ParseBody(message, f, NULL, NULL);
- fclose(f);
- }
- unlink(newfile);
- }
-
- ParsePartial(message, input)
- struct MessageInfo *message;
- FILE *input;
- {
- int numsort(), numselect(), atoi();
- int i, idi, numi, totali, numparts;
- struct direct **files;
- char storagedir[MAXFILENAMELEN], storagefile[MAXFILENAMELEN], cmd[256];
- FILE *fopen(), *ct;
-
- /* extract certain parameters from the content-type field */
- idi = numi = totali = -1;
- for(i=0; i < message->content.numparms; i++)
- if (!strcasecmp(message->content.parms[i].name, "id")) idi=i;
- else if (!strcasecmp(message->content.parms[i].name, "number")) numi=i;
- else if (!strcasecmp(message->content.parms[i].name, "total")) totali=i;
-
- if (idi >= 0 && numi >= 0) {
- sprintf(storagedir, "%s/%s", TEMPDIR, message->content.parms[idi].value);
- /* create the directory if it doesn't exist. For now, this approach
- doesn't deal well with mkdir errors */
- mkdir(storagedir, 0700);
-
- /* this strategy for storing pieces doesn't follow the MIME document's
- prescription for creating the resulting header, but it does follow the
- de facto method suggested by metamail's splitmail(1) command */
- sprintf(storagefile, "%s/%s", storagedir, message->content.parms[numi].value);
- StoreStream(NULL, input, storagefile, NULL, NULL);
- sprintf(storagefile, "%s/CT", storagedir);
- if (totali >= 0) {
- ct = fopen(storagefile, "w");
- fputs(message->content.parms[totali].value, ct);
- fclose(ct);
- }
-
- /* okay, now see if all parts have arrived */
- ct = fopen(storagefile, "r");
- if (ct != NULL) { /* we must know the total */
- fscanf(ct, "%d", &numparts);
- fclose(ct);
- if (numparts == scandir(storagedir, &files, numselect, numsort)) {
- sprintf(cmd, "cd %s; cat", storagedir);
- while (numparts--) { /* This could be more efficient by using an endpointer */
- strcat(cmd, " ");
- strcat(cmd, (*files++)->d_name);
- }
-
- /* now, recursively process the message -- the current message will end
- up being ignored since it is a message/partial */
- ct = popen(cmd, "r");
- HandleMessage(ct);
- pclose(ct);
- sprintf(cmd, "rm -r %s", storagedir);
- system(cmd);
- }
- }
- }
- else ErrorExit("Partial message must have id and number parameters.");
- }
-
- /* the next three functions are support for the scandir call made in ParsePartial */
-
- int numselect(d)
- struct direct *d;
- {
- char *s;
-
- for (s=d->d_name; *s; s++) if (!isdigit(*s)) return 0;
- return 1;
- }
-
- int numsort(d1, d2)
- struct direct **d1, **d2;
- {
- return(icomp(atoi((*d1)->d_name), atoi((*d2)->d_name)));
- }
-
- int icomp(i1, i2)
- int i1, i2;
- {
- return(i1<i2 ? -1 : (i1>i2));
- }
-
- StoreStream(encoding, instream, fname, boundaries, numboundlval)
- char *encoding;
- FILE *instream;
- char *fname;
- char **boundaries;
- int *numboundlval;
- {
- FILE *outstream;
- FILE *fopen();
-
- outstream = fopen(fname, "w");
- if (encoding && !strcasecmp(encoding, "base64"))
- from64(instream, outstream, boundaries, numboundlval);
- else if (encoding && !strcasecmp(encoding, "quoted-printable"))
- fromqp(instream, outstream, boundaries, numboundlval);
- else
- from7bit(instream, outstream, boundaries, numboundlval);
- fclose(outstream);
- }
-
- MakeFileName(buf)
- char *buf;
- {
- static int ctr = 0;
- sprintf(buf, "part%d", ctr++);
- }
-
- FreeGarbage(message)
- struct MessageInfo message;
- {
- }
-
- from7bit(infile, outfile, boundaries, boundaryct)
- FILE *infile, *outfile;
- char **boundaries;
- int *boundaryct;
- {
- char buf[1000], *b;
- int sawnewline = 2;
-
- while (fgets(buf, sizeof(buf), infile) != NULL) {
- if (sawnewline && boundaries && buf[0] == '-' && buf[1] == '-' &&
- PendingBoundary(buf, boundaries, boundaryct)) return;
- else {
- if (outfile) {
- if (sawnewline == 1) putc('\n', outfile);
- sawnewline = 0;
- for(b=buf; b; b++)
- if (*b == '\n') { sawnewline = 1; break; }
- else putc(*b, outfile);
- }
- else sawnewline = (buf[strlen(buf)-1] == '\n');
- }
- }
- }
-